Amazon Aurora DSQLが発表されたのでdrizzleから繋いでみた #AWSreInvent

Amazon Aurora DSQLが発表されたのでdrizzleから繋いでみた #AWSreInvent

re:Invent 2024でpostgres互換のサーバーレス分散SQLデータベースであるAmazon Aurora DSQLが発表されました! なので早速サーバーレス指向ORMであるdrizzle ORMを使ってmigrationと接続を試してみました!
Clock Icon2024.12.04

製造ビジネステクノロジー部のやまたつです!

re:Invent 2024でpostgres互換のサーバーレス分散SQLデータベースであるAmazon Aurora DSQLが発表されました!
なので早速サーバーレス指向ORMであるdrizzle ORMを使ってmigrationと接続を試してみました!

コードは以下のリポジトリに置いてあります。

https://github.com/yamatatsu/play-dsql-drizzle

前提

基本的な設定はdrizzle公式のpostgresqlのドキュメントに従っています。

https://orm.drizzle.team/docs/get-started/postgresql-new

公式通りの実装では動作しなかった点を中心に、以下に示していきます。

実装

src/db/schema.ts

まずはスキーマを定義します。

drizzleのGet Startedではid列の定義としてinteger().primaryKey().generatedAlwaysAsIdentity(),を使っているのですが、Aurora DSQLはGENERATED ALWAYS AS IDENTITYが使えないようで、以下のようなエラーが発生してしまいます。

error: IDENTITY constraint is not supported

なので代わりにvarchar().primaryKey().$default(() => randomUUID())を使うようにしました。
(後で結局.$default(() => randomUUID())は使わないコードになってしまったが。。)

import { randomUUID } from "node:crypto";
import { integer, pgTable, varchar } from "drizzle-orm/pg-core";

export const usersTable = pgTable("users", {
	id: varchar().primaryKey().$default(() => randomUUID()),
	name: varchar({ length: 255 }).notNull(),
	age: integer().notNull(),
	email: varchar({ length: 255 }).notNull().unique(),
});

drizzle.config.ts

次にdrizzleの接続設定です。

Aurora DSQLでは、15分ごとに有効期限切れになるAuthentication tokenをpasswardとして使う必要があります。
参考: https://docs.aws.amazon.com/aurora-dsql/latest/userguide/SECTION_authentication-token.html

.env上で簡単に交換できるように、databaseUrl形式ではなく、host,database,user,passwordそれぞれを指定する形式に変更しました。
(prismaではこれができないのがめんどくさいんよな。。。)

import "dotenv/config";
import { defineConfig } from "drizzle-kit";

export default defineConfig({
	out: "./drizzle",
	schema: "./src/db/schema.ts",
	dialect: "postgresql",
	dbCredentials: {
		host: process.env.DB_HOST!,
		database: process.env.DB_NAME!,
		user: process.env.DB_USER!,
		password: process.env.DB_PASSWORD!,
		ssl: true,
	},
});

.env

以下のように環境変数を定義します。

Authentication tokenは公式ドキュメントを参考にwebコンソールから取得しました。
(aws cliで取得しようとしたけど、筆者が試した限りでは、更新してもまだaws dsqlが生えてなかった。。。)

DB_HOST=xxxxxxxxxxxxxxxxxxxxxxxxxx.dsql.us-east-1.on.aws
DB_USER=admin
DB_NAME=postgres
DB_PASSWORD='<<手に入れたAuthentication token>>'

migrationしてみる

$ npx drizzle-kit push

[] Pulling schema from database...
[] Changes applied

migrationできました!

データを追加したり参照したりしてみる

src/index.tsを以下のように記述します。
何度も実行できるようにupsertで実装しました。

insertとselectはそれぞれconsole.time()を使って計測してみます。

import "dotenv/config";
import { drizzle } from "drizzle-orm/node-postgres";
import { usersTable } from "./db/schema";

const db = drizzle({
	connection: {
		host: process.env.DB_HOST,
		database: process.env.DB_NAME,
		user: process.env.DB_USER,
		password: process.env.DB_PASSWORD,
		ssl: true,
	},
});

main().then(
	() => {
		console.info("Done");
		process.exit(0);
	},
	(err) => {
		console.error(err);
		process.exit(1);
	},
);

async function main() {
	const users = [
		{
			id: "979d1120-db93-4c5e-b631-36f00fd46607",
			name: "John Doe",
			age: 30,
			email: "foo@example.com",
		},
		{
			id: "b35efca4-e902-4531-aad2-97d86e553582",
			name: "Jane Doe",
			age: 25,
			email: "bar@example.com",
		},
		{
			id: "b10c2fd5-be46-4b15-bd50-2043dcd394b3",
			name: "Alice",
			age: 20,
			email: "buz@example.com",
		},
	] satisfies (typeof usersTable.$inferInsert)[];

	for (const user of users) {
		console.time("Inserting a user");
		await db.insert(usersTable).values(user).onConflictDoUpdate({
			target: usersTable.id,
			set: user,
		});
		console.timeEnd("Inserting a user");
	}

	console.time("Selecting all users");
	const selected = await db.select().from(usersTable);
	console.timeEnd("Selecting all users");

	console.log("Users:", selected);
}

それでは実行してみます。

$ pnpm tsx ./src/index.ts

Inserting a user: 793.159ms
Inserting a user: 85.274ms
Inserting a user: 81.759ms
Selecting all users: 87.706ms
Users: [
  {
    id: '979d1120-db93-4c5e-b631-36f00fd46607',
    name: 'John Doe',
    age: 30,
    email: 'foo@example.com'
  },
  {
    id: 'b10c2fd5-be46-4b15-bd50-2043dcd394b3',
    name: 'Alice',
    age: 20,
    email: 'buz@example.com'
  },
  {
    id: 'b35efca4-e902-4531-aad2-97d86e553582',
    name: 'Jane Doe',
    age: 25,
    email: 'bar@example.com'
  }
]
Done

保存されたデータが取得されました!
初回のinsertはコネクションの確立があるためか、1秒弱かかりますが、2回目以降は100ms以下で実行できていました。
ちゃんとした計測ではないので、もう少し詳しく計測してみたいですね。

まとめ

「drizzleでRDS Data APIを触るの、試してみたいなー」と思っていたところに今回の発表があったので、早速試してみました。
cold start時のオーバーヘッドが気になりますが、lambdaからも使ってみたいと思いました!

以上でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.